// Tcp服务器 -- 支持网络计算器业务
// 服务器我们让它干啥 ??
// 负责通信 , 即 : 将客户端给的计算业务的结果返回给客户端

// 前提知识 : 多进程时一定要记得关闭各自不用的文件描述符哦 !

#pragma once

#include "Common.hpp"
#include "Socket.hpp"
#include "Log.hpp"
#include <memory>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <functional>

using namespace SocketModel;
using namespace LogModule;

using ioservice_t = std::function<void(std::shared_ptr<Socket> &sock, InteInfo &client)>;

class TcpServer
{
public:
    TcpServer(uint16_t port, ioservice_t func)
        : _port(port), _isrunning(false), _ios_func(func), _listen_socket_ptr(std::make_unique<TCP>())
    {
#if 0 
        //防止让父进程阻塞等待的方式一: 忽略子进程 SINGCHLD信号
        //因为子进程退出会给父进程发信号 , 忽略这信号子进程不用被等待 ,系统会回收
        signal(SIGCHLD , SIG_IGN);
#endif

        // 这里初始化服务器
        // 创建套接字  -- 方法里面包含了创建方法
        // 1.socket 2. bind 3. listen
        _listen_socket_ptr->BuildTcpSocketMethod(_port);
    }

    ~TcpServer() {}

    // 服务器启动 -- 只负责通信功能
    // 这里因为计算器是长服务 , 所以实现用多进程实现
    void Start()
    {

        _isrunning = true;
        // 服务器运行起来要先接受客户端发的数据吧 ?? 然后在发回
        // 要先获取连接
        while (_isrunning)
        {
            // 要知道获取谁的连接 -- 创建网络信息
            InteInfo client; // Accept这个把信息带回来
            // Accept(&client);会返回一个套接字
            // socket 是普通文件描述符构造回来的套接字
            auto socket = _listen_socket_ptr->Accept(&client);
            if (socket == nullptr)
            {
                // 获取连接失败 , 那继续获取
                continue;
            }
            // 获取连接成功
            LOG(LogLevel::DEBUG) << "accept success ...";

            // 获取成功,就要处理了 , 这里使用多进程
            // 但是子进程能看到父进程打开的文件描述符 , 所以要关闭各自不需要的
            pid_t id = fork();
            if (id < 0)
            {
                // 异常
                LOG(LogLevel::FATAL) << "fork error ...";
                exit(FORK_ERROR);
            }
            else if (id == 0)
            {
                // 子进程

                // 关闭 listen_sockfd
                _listen_socket_ptr->Close();

                if (fork() > 0)
                    exit(OK);
                // 孙子进程继续完成任务 , 其为孤儿进程 , 被系统领养

                // 这里完成任务 , 任务也不是服务器完成 , 让上层完成
                _ios_func(socket, client);
                socket->Close();
                exit(OK);
            }
            else
            {
                // 父进程
                // 关闭 sockfd , socket 就是普通文件描述符构造回来的
                socket->Close();

                waitpid(id, nullptr, 0); // 阻塞等待
            }
        }
        _isrunning = false;
    }

private:
    uint16_t _port;
    // listen 套接字
    std::unique_ptr<Socket> _listen_socket_ptr;

    bool _isrunning; // 是否运行

    // 生层任务
    ioservice_t _ios_func;
};